package com.cmc.cloud.cmclink.doc.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.cmc.cloud.cmclink.doc.api.document.emums.*;
import com.cmc.cloud.cmclink.doc.constants.RedisKeyConstants;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.BlnoErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.SpecialCargoApplyErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.convert.AttachmentConvert;
import com.cmc.cloud.cmclink.doc.convert.SpecialCargoCntrConvert;
import com.cmc.cloud.cmclink.doc.convert.SpecialCargoVoyageConvert;
import com.cmc.cloud.cmclink.doc.entity.*;
import com.cmc.cloud.cmclink.doc.mapper.*;
import com.cmc.cloud.cmclink.doc.rpcservice.VoyScheduleService;
import com.cmc.cloud.cmclink.doc.service.ApplySaveOrUpdateService;
import com.cmc.cloud.cmclink.doc.service.impl.so.AttachmentServiceImpl;
import com.cmc.cloud.cmclink.doc.service.impl.so.SoForecastServiceImpl;
import com.cmc.cloud.cmclink.doc.util.RedisUtils;
import com.cmc.cloud.cmclink.doc.util.VslvoyUtil;
import com.cmc.cloud.cmclink.doc.vo.blnovslvoyvo.BlnoVslvoyUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.attachmentvo.AttachmentRespVO;
import com.cmc.cloud.cmclink.doc.vo.sovo.attachmentvo.AttachmentUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.specialcargoapplyvo.SpecialCargoVoyageUpdateReqVO;
import com.cmc.cloud.cmclink.doc.vo.specialcargocntrvo.SpecialCargoCntrBaseVO;
import com.cmc.cloud.cmclink.svl.api.base.enums.SystemEnums;
import com.cmc.cloud.cmclink.svl.api.base.req.VesselVoyReqDTO;
import com.cmc.cloud.cmclink.svl.api.base.resp.VesselVoyRespDTO;
import com.cmes.framework.common.exception.util.ServiceExceptionUtil;
import com.cmes.framework.mybatis.core.query.LambdaQueryWrapperX;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.time.Year;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;


/**
 * 特种货审批主信息 Service 实现类
 *
 * @author 集运管理员
 */
@Service
@Validated
public class ApplySaveOrUpdateServiceImpl implements ApplySaveOrUpdateService {

    private static final String CNSHA = "CNSHA";
    private static final String SCHEDULESTATUS_P = "P";

    @Resource
    private SpecialCargoApplyMapper specialCargoApplyMapper;
    @Resource
    private SpecialCargoVoyageMapper specialCargoVoyageMapper;
    @Resource
    private SpecialCargoCntrMapper specialCargoCntrMapper;
    @Resource
    private SpecialCargoDgMapper specialCargoDgMapper;
    @Resource
    private CargoCntrTypeRelationMapper cargoCntrTypeRelationMapper;
    @Resource
    private AttachmentServiceImpl attachmentService;
    @Resource
    private RedisUtils redisUtils;
    @Resource
    private SoForecastServiceImpl soForecastService;
    @Resource
    private VslvoyUtil vslvoyUtil;
    @Resource
    private VoyScheduleService voyScheduleService;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long saveOrUpdateApply(SpecialCargoApplyDO applyDO) {
        //检查是否是可编辑状态
        if (null != applyDO.getId()) {
            List<String> editStatus = Arrays.asList(ApplyStatusEnum.EDIT.getValue(), ApplyStatusEnum.ADD_INFO.getValue(), ApplyStatusEnum.ADD_ATTACHMENT.getValue());
            SpecialCargoApplyDO specialCargoApplyDO = specialCargoApplyMapper.selectById(applyDO.getId());
            if (!editStatus.contains(specialCargoApplyDO.getApplyStatus())) {
                throw ServiceExceptionUtil.exception(SpecialCargoApplyErrorCodeConstants.APPLY_STATUS_EDIT_ERROR);
            }
        }
        applyDO.setCurrentPort(applyDO.getPolCode());
        if (StringUtils.isEmpty(applyDO.getApplyNo())) {
            applyDO.setApplyNo(getApplyNo(applyDO.getApplyType()));
        }
        if (StringUtils.isEmpty(applyDO.getApplyStatus())) {
            applyDO.setApplyStatus(ApplyStatusEnum.EDIT.getValue());
        }
        if (null == applyDO.getId()) {
            specialCargoApplyMapper.insert(applyDO);
        } else {
            specialCargoApplyMapper.updateById(applyDO);
        }
        return applyDO.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveOrUpdateVoyage(List<SpecialCargoVoyageUpdateReqVO> spcVoyageInfo, Long applyId, Boolean partnerFlag) {
        if (CollectionUtil.isNotEmpty(spcVoyageInfo)) {
            if (null != partnerFlag && partnerFlag) {
                spcVoyageInfo.forEach(voyage -> {
                    voyage.setCarrier(CntrOwnerEnum.SNL.getValue());
                    voyage.setRouteApproveStatus(ApprovalStatusEnum.PENDING.getValue());
                    voyage.setPolEtd(StringUtils.EMPTY);
                    voyage.setApplyId(applyId);
                });
            } else {
                spcVoyageInfo.forEach(voyage -> {
                    voyage.setPolEtd(StringUtils.EMPTY);
                    voyage.setApplyId(applyId);
                });
            }
            List<SpecialCargoVoyageDO> voyageDos = SpecialCargoVoyageConvert.INSTANCE.convertDOList(spcVoyageInfo);
            specialCargoVoyageMapper.saveOrUpdateBatch(voyageDos);
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long saveOrUpdateCntr(SpecialCargoCntrBaseVO cntrBaseInfo, Long applyId) {
        SpecialCargoCntrDO cntrDO = SpecialCargoCntrConvert.INSTANCE.convert(cntrBaseInfo);
        cntrDO.setApplyId(applyId);
        if (null == cntrDO.getId()) {
            specialCargoCntrMapper.insert(cntrDO);
        } else {
            specialCargoCntrMapper.updateById(cntrDO);
        }
        return cntrDO.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void spcAttachmentSaveOrUpdate(List<AttachmentUpdateReqVO> spcAttachmentList, Long applyId) {
        if (CollectionUtil.isEmpty(spcAttachmentList)) {
            return;
        }
        List<AttachmentUpdateReqVO> filteredList = spcAttachmentList.stream().filter(attachment -> null != attachment.getFileId()).collect(Collectors.toList());
        List<AttachmentRespVO> oldAttachments = AttachmentConvert.INSTANCE.convertList(attachmentService.seleteByReferenceId(applyId, SpecialEnum.SPC.getValue()));
        attachmentService.createOrUpdateAndDeleteBatch(applyId, SpecialEnum.SPC.getValue(), filteredList, oldAttachments);
    }

    @Override
    public void removeCntr(Long applyId, List<Long> argCntrIds) {
        List<SpecialCargoCntrDO> cntrDOList = specialCargoCntrMapper.selectList(new LambdaQueryWrapper<SpecialCargoCntrDO>().eq(SpecialCargoCntrDO::getApplyId, applyId));
        List<Long> cntrIds = cntrDOList.stream().map(SpecialCargoCntrDO::getId).collect(Collectors.toList());
        List<Long> needRemoveCntrIds = cntrIds.stream().filter(element -> !argCntrIds.contains(element)).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(needRemoveCntrIds)) {
            specialCargoCntrMapper.deleteBatchIds(needRemoveCntrIds);
        }
    }

    @Override
    public void removeDg(Long applyId, List<Long> argDgIds) {
        List<SpecialCargoDgDO> dgDOList = specialCargoDgMapper.selectList(new LambdaQueryWrapper<SpecialCargoDgDO>().eq(SpecialCargoDgDO::getApplyId, applyId));
        List<Long> dgIds = dgDOList.stream().map(SpecialCargoDgDO::getId).collect(Collectors.toList());
        List<Long> needRemoveDgIds = dgIds.stream().filter(element -> !argDgIds.contains(element)).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(needRemoveDgIds)) {
            specialCargoDgMapper.deleteBatchIds(needRemoveDgIds);
            List<CargoCntrTypeRelationDO> cntrDgRelations = cargoCntrTypeRelationMapper.selectList(new LambdaQueryWrapperX<CargoCntrTypeRelationDO>()
                    .eq(CargoCntrTypeRelationDO::getApplyId, applyId).in(CargoCntrTypeRelationDO::getDgId, needRemoveDgIds));
            if (CollectionUtil.isNotEmpty(cntrDgRelations)) {
                cargoCntrTypeRelationMapper.deleteBatchIds(cntrDgRelations);
            }
        }
    }

    @Override
    public void checkCntrSizeType(Long soId, List<SpecialCargoCntrBaseVO> cntrList) {
        if (null != soId) {
            List<SoForecastDO> soForecastDos = soForecastService.seleteBySoId(soId);
            if (CollectionUtil.isNotEmpty(soForecastDos) && CollectionUtil.isNotEmpty(cntrList)) {
                Map<String, Long> cntrProperties = cntrList.stream()
                        .collect(Collectors.groupingBy(
                                n -> n.getCntrType() + "-" + n.getCntrSize(),
                                Collectors.summingLong(SpecialCargoCntrBaseVO::getQty)
                        ));
                List<String> soCntrInfo = soForecastDos.stream().map(n -> n.getCntrType() + "-" + n.getCntrSize()).distinct().collect(Collectors.toList());
                Map<String, Long> soCntrMap = soForecastDos.stream().collect(Collectors.toMap(n -> n.getCntrType() + "-" + n.getCntrSize()
                        , SoForecastDO::getCntrQuantity, (existing, replacement) -> existing));
                for (String key : cntrProperties.keySet()) {
                    if (!soCntrInfo.contains(key)) {
                        throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_SO_CNTR_NOT_EQUAL);
                    }
                    Long spcNum = cntrProperties.get(key);
                    Long soNum = soCntrMap.get(key);
                    if (spcNum.compareTo(soNum) > 0) {
                        throw ServiceExceptionUtil.exception(BlnoErrorCodeConstants.SI_LESS_THEN_SO);
                    }
                }

            }
        }
    }

    public String getApplyNo(String applyType) {
        int currentYear = Year.now().getValue();
        Long atomicLong = redisUtils.getAtomicLong(RedisKeyConstants.DOC_SPECIAL_APPLY_KEY + applyType + currentYear);
        return applyType + currentYear + String.format("%06d", atomicLong);
    }

    @Override
    public boolean updateTransitCnsha(String polCode, String podCode, List<SpecialCargoVoyageUpdateReqVO> voyageInfo) {
        if (CNSHA.equals(polCode) || CNSHA.equals(podCode)) {
            return true;
        }
        for (SpecialCargoVoyageUpdateReqVO voyage : voyageInfo) {
            if (CNSHA.equals(voyage.getPolCode()) || CNSHA.equals(voyage.getPodCode())) {
                return true;
            } else {
                if(!TransmodeEnum.LINER.getValue().equals(voyage.getTransmode())){
                    continue;
                }
                VesselVoyReqDTO reqDto = new VesselVoyReqDTO();
                reqDto.setVesselCodeList(Collections.singletonList(voyage.getVesselCode()));
                reqDto.setVoy(voyage.getVoyageEx());
                reqDto.setSystemNumber(SystemEnums.IBS.getSystemNumber());
                List<VesselVoyRespDTO> vesselVoyResp = voyScheduleService.listVesselVoy(reqDto);
                if(CollectionUtil.isNotEmpty(vesselVoyResp)){
                    return vesselVoyResp.stream()
                            .anyMatch(dto -> CNSHA.equals(dto.getPortCode()));
                }
            }
        }
        return false;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateVoyageInfo(String bookingNo, List<BlnoVslvoyUpdateReqVO> voyageInfo) {
        if (StringUtils.isEmpty(bookingNo) || CollectionUtil.isEmpty(voyageInfo)) {
            return;
        }
        LambdaQueryWrapperX<SpecialCargoApplyDO> specialCargoApplyWrapperX = new LambdaQueryWrapperX<>();
        specialCargoApplyWrapperX.eq(SpecialCargoApplyDO::getBookingNo, bookingNo)
                .ne(SpecialCargoApplyDO::getApplyStatus, ApplyStatusEnum.APPLY_CANCELLED.getValue())
                .orderByDesc(SpecialCargoApplyDO::getCreateTime).last("LIMIT 1");
        SpecialCargoApplyDO specialCargoApplyDo = specialCargoApplyMapper.selectOne(specialCargoApplyWrapperX);
        if (null == specialCargoApplyDo) {
           return;
        }
        LambdaQueryWrapperX<SpecialCargoVoyageDO> voyageWrapper = new LambdaQueryWrapperX<>();
        voyageWrapper.eq(SpecialCargoVoyageDO::getApplyId, specialCargoApplyDo.getId());
        specialCargoVoyageMapper.delete(voyageWrapper);
        List<SpecialCargoVoyageDO> specialCargoVoyageDo = new ArrayList<>();
        voyageInfo.forEach(item -> {
            SpecialCargoVoyageDO voyageDo = SpecialCargoVoyageConvert.INSTANCE.convertVoyageDos(item);
            voyageDo.setId(null);
            voyageDo.setApplyId(specialCargoApplyDo.getId());
            voyageDo.setCarrier(item.getVesselOperator());
            voyageDo.setLaneCode(item.getLaneCodeEx());
            voyageDo.setRouteApproveStatus(ApprovalStatusEnum.PENDING.getValue());
            specialCargoVoyageDo.add(voyageDo);
        });
        specialCargoVoyageMapper.insertBatch(specialCargoVoyageDo);
        specialCargoApplyDo.setApplyStatus(ApplyStatusEnum.CHANGEVOYCHECK.getValue());
        specialCargoApplyMapper.updateById(specialCargoApplyDo);

    }

}
